Skip to content

container: T7736: give container veths a deterministic host_interface_name - #5352

Merged
c-po merged 2 commits into
vyos:rollingfrom
c-po:container-veth
Aug 5, 2026
Merged

container: T7736: give container veths a deterministic host_interface_name#5352
c-po merged 2 commits into
vyos:rollingfrom
c-po:container-veth

Conversation

@c-po

@c-po c-po commented Jul 25, 2026

Copy link
Copy Markdown
Member

Change summary

Podman's default vethN auto-naming for a container's host-side veth can collide with VyOS's own virtual-ethernet vethN interfaces.

Bump the minimum Podman dependency to 5.8 (which supports the "host_interface_name" network connect option) and use it to name every non-macvlan container network attachment veth-<container name> instead, eliminating the collision by construction.

Types of changes

  • Bug fix (non-breaking change which fixes an issue)
  • New feature (non-breaking change which adds functionality)
  • Code style update (formatting, renaming)
  • Refactoring (no functional changes)
  • Migration from an old Vyatta component to vyos-1x, please link to related PR inside obsoleted component
  • Other (please describe):

Related Task(s)

https://vyos.dev/T7736

Related PR(s)

How to test / Smoketest result

 INFO - Executing VyOS smoketests
DEBUG - vyos@vyos:~$ /usr/bin/vyos-smoketest
DEBUG - /usr/bin/vyos-smoketest
DEBUG - Running Testcase (1/1): /usr/libexec/vyos/tests/smoke/cli/test_container.py
DEBUG - test_api_socket (__main__.TestContainer.test_api_socket) ... ok
DEBUG - test_basic (__main__.TestContainer.test_basic) ... ok
DEBUG - test_cpu_limit (__main__.TestContainer.test_cpu_limit) ... ok
DEBUG - test_dual_stack_network (__main__.TestContainer.test_dual_stack_network) ... ok
DEBUG - test_healthcheck (__main__.TestContainer.test_healthcheck) ... ok
DEBUG - test_ipv4_network (__main__.TestContainer.test_ipv4_network) ... ok
DEBUG - test_ipv6_network (__main__.TestContainer.test_ipv6_network) ... ok
DEBUG - test_long_name_host_interface_uniqueness (__main__.TestContainer.test_long_name_host_interface_uniqueness) ... ok
DEBUG - test_name_server (__main__.TestContainer.test_name_server) ... ok
DEBUG - test_network_mtu (__main__.TestContainer.test_network_mtu) ... ok
DEBUG - test_network_types (__main__.TestContainer.test_network_types) ... ok
DEBUG - test_network_vrf (__main__.TestContainer.test_network_vrf) ... ok
DEBUG - test_no_name_server (__main__.TestContainer.test_no_name_server) ... ok
DEBUG - test_uid_gid (__main__.TestContainer.test_uid_gid) ... ok
DEBUG - test_user_defined_mac (__main__.TestContainer.test_user_defined_mac) ... ok
DEBUG -
DEBUG - ----------------------------------------------------------------------
DEBUG - Ran 15 tests in 209.184s
DEBUG -
DEBUG - OK
DEBUG - SUCCESS! All 1 tests passed.

Checklist:

  • I have read the CONTRIBUTING document
  • I have linked this PR to one or more Phabricator Task(s)
  • I have run the components SMOKETESTS if applicable
  • I have thoroughly reviewed, understood, and tested the code contained in the PR, including any code produced by GenAI tools
  • My commit headlines contain a valid Task id
  • My change requires a change to the documentation
  • I have updated the documentation accordingly

@coderabbitai

coderabbitai Bot commented Jul 25, 2026

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Summary by CodeRabbit

  • New Features
    • Added commands to display host-side interfaces used by containers, with table and structured output.
    • Improved container networking with deterministic, collision-resistant interface names, including support for long container names.
    • Added validation to detect conflicting interface names before startup.
  • Tests
    • Added coverage for long-name uniqueness and interface collision handling.
  • Requirements
    • Podman 5.8+, Netavark 1.14.0+, and Aardvark-DNS 1.14.0+ are now required.

Walkthrough

Container networking now assigns deterministic host-side interface names for non-macvlan networks, validates collisions, emits explicit Podman network options, and exposes interface status commands. The package now requires Podman 5.8, netavark 1.14.0, and aardvark-dns 1.14.0.

Changes

Container networking

Layer / File(s) Summary
Host interface naming and validation
python/vyos/container.py, src/conf_mode/container.py
Generates bounded deterministic host interface names and rejects collisions for non-macvlan container networks.
Network argument generation
src/conf_mode/container.py
Passes network configuration into runtime argument generation, adds host_interface_name for non-macvlan networks, and emits explicit space-separated --network options.
Interface status commands
src/op_mode/container.py, op-mode-definitions/container.xml.in
Adds table and raw commands that report configured container networks and host interfaces.
Runtime compatibility validation
smoketest/scripts/cli/test_container.py, debian/control
Tests long-name uniqueness and collision rejection. Updates Podman, netavark, and aardvark-dns version requirements.
🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Title check ✅ Passed The title clearly summarizes the main change: deterministic host-side interface names for container veths.
Description check ✅ Passed The description directly explains the veth collision fix, dependency changes, implementation, testing, and related task.
✨ Finishing Touches
✨ Simplify code
  • Create PR with simplified code

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@src/conf_mode/container.py`:
- Around line 364-373: The get_container_host_ifname function must avoid
collisions caused by truncating container names to 15 characters. Generate a
deterministic host interface name using a bounded hash suffix (or otherwise
guarantee uniqueness), and update verify() to reject any duplicate generated
names if needed. Add a regression test covering distinct long names such as
abcdefghij-1 and abcdefghij-2.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository YAML (base), Central YAML (inherited), Organization UI (inherited)

Review profile: CHILL

Plan: Pro

Run ID: 87df2d00-9496-48eb-aa88-ce7b0312771f

📥 Commits

Reviewing files that changed from the base of the PR and between d50098c and 1ac13ed.

📒 Files selected for processing (2)
  • debian/control
  • src/conf_mode/container.py
🔗 Linked repositories identified

CodeRabbit considers these linked repositories for cross-repo context during reviews:

  • ansible/ansible (manual)
📜 Review details
⏰ Context from checks skipped due to timeout. (4)
  • GitHub Check: build_iso
  • GitHub Check: codeql-analysis-call / Analyze (python)
  • GitHub Check: Mergify Merge Protections
  • GitHub Check: Summary
⚠️ CI failures not shown inline (2)

GitHub Actions: Python Lint (Darker + Ruff) / darker-ruff-lint _ darker-ruff-lint: container: T7736: give container veths a deterministic host_interface_name

Conclusion: failure

View job details

##[group]Run echo "### 🧪 Lint Results"
 �[36;1mecho "### 🧪 Lint Results"�[0m
 �[36;1mdarker_failed="1"�[0m
 �[36;1mgraylint_failed=""�[0m
 �[36;1m�[0m
 �[36;1mif [[ "$darker_failed" == "1" ]]; then�[0m
 �[36;1m  echo "- ❌ **Darker** check failed"�[0m
 �[36;1melse�[0m
 �[36;1m  echo "- ✅ **Darker** check passed"�[0m
 �[36;1mfi�[0m
 �[36;1m�[0m
 �[36;1mif [[ "$graylint_failed" == "1" ]]; then�[0m
 �[36;1m  echo "- ❌ **Graylint (ruff check)** failed"�[0m
 �[36;1melse�[0m
 �[36;1m  echo "- ✅ **Graylint (ruff check)** passed"�[0m
 �[36;1mfi�[0m
 �[36;1m�[0m
 �[36;1mif [[ "$darker_failed" == "1" || "$graylint_failed" == "1" ]]; then�[0m
 �[36;1m  echo "::error::One or more linters failed. See above for details."�[0m

GitHub Actions: Python Lint (Darker + Ruff) / 0_darker-ruff-lint _ darker-ruff-lint.txt: container: T7736: give container veths a deterministic host_interface_name

Conclusion: failure

View job details

##[group]Run echo "### 🧪 Lint Results"
 �[36;1mecho "### 🧪 Lint Results"�[0m
 �[36;1mdarker_failed="1"�[0m
 �[36;1mgraylint_failed=""�[0m
 �[36;1m�[0m
 �[36;1mif [[ "$darker_failed" == "1" ]]; then�[0m
 �[36;1m  echo "- ❌ **Darker** check failed"�[0m
 �[36;1melse�[0m
 �[36;1m  echo "- ✅ **Darker** check passed"�[0m
 �[36;1mfi�[0m
 �[36;1m�[0m
 �[36;1mif [[ "$graylint_failed" == "1" ]]; then�[0m
 �[36;1m  echo "- ❌ **Graylint (ruff check)** failed"�[0m
 �[36;1melse�[0m
 �[36;1m  echo "- ✅ **Graylint (ruff check)** passed"�[0m
 �[36;1mfi�[0m
 �[36;1m�[0m
 �[36;1mif [[ "$darker_failed" == "1" || "$graylint_failed" == "1" ]]; then�[0m
 �[36;1m  echo "::error::One or more linters failed. See above for details."�[0m
🧰 Additional context used
📓 Path-based instructions (2)
src/conf_mode/*.py

📄 CodeRabbit inference engine (AGENTS.md)

Conf-mode entry-point scripts must be named after CLI components and located in src/conf_mode/

Files:

  • src/conf_mode/container.py
**/*.py

📄 CodeRabbit inference engine (AGENTS.md)

**/*.py: Use ruff 0.6.4 for Python linting with configuration in ruff.toml at repository root
Use pylint to check for W0611 (unused imports) violations in Python code
Use darker for code formatting in Python files
Use nose2 for Python testing with configuration in nose2.cfg at repository root

Files:

  • src/conf_mode/container.py
🔍 Remote MCP

Relevant Podman docs for this PR:

  • host_interface_name is documented for bridge networks: --network=bridge:host_interface_name=<name> sets the external/host-side interface name. citeContext7::Context7_query-docs::
  • Podman’s network docs also say interface_name is the container-side name, while host_interface_name is the outside/container-host name. citeContext7::Context7_query-docs::
  • For macvlan/ipvlan, Podman docs describe the option as selecting the parent host device (--opt parent=... / --interface-name), not a host-side veth pair. That supports the PR’s choice to avoid assigning host_interface_name for macvlan attachments. citeContext7::Context7_query-docs::

I did not find version-provenance for when host_interface_name was introduced in the retrieved docs.

🔇 Additional comments (3)
src/conf_mode/container.py (2)

526-540: LGTM!

Also applies to: 551-552, 575-575, 655-661


376-376: 🎯 Functional Correctness

No additional callers need changessrc/conf_mode/container.py:661 already passes network_config, and no other generate_run_arguments() calls exist.

			> Likely an incorrect or invalid review comment.
debian/control (1)

335-335: 🎯 Functional Correctness

podman (>= 5.8) is sufficient in debian/control:335
host_interface_name is available for bridge/user-defined network use, so no version bump is needed.

Comment thread src/conf_mode/container.py Outdated
@c-po
c-po force-pushed the container-veth branch from 1ac13ed to c21e337 Compare July 25, 2026 12:12

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@smoketest/scripts/cli/test_container.py`:
- Around line 267-293: The test_long_name_host_interface_uniqueness test
currently verifies only PID existence, so it must also validate the
host-interface contract and container state. After cli_commit, obtain each
container’s actual host-side interface through the existing Podman or systemd
inspection mechanism, assert both interfaces are present and distinct, and
verify each expected container is running via Podman or systemd rather than
relying on /proc/<pid> alone.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository YAML (base), Central YAML (inherited), Organization UI (inherited)

Review profile: CHILL

Plan: Pro

Run ID: bc675035-0c27-46ef-8715-59a0a0e3f615

📥 Commits

Reviewing files that changed from the base of the PR and between 1ac13ed and c21e337.

📒 Files selected for processing (3)
  • debian/control
  • smoketest/scripts/cli/test_container.py
  • src/conf_mode/container.py
🔗 Linked repositories identified

CodeRabbit considers these linked repositories for cross-repo context during reviews:

  • ansible/ansible (manual)
🚧 Files skipped from review as they are similar to previous changes (1)
  • debian/control
📜 Review details
⏰ Context from checks skipped due to timeout. (5)
  • GitHub Check: build_iso
  • GitHub Check: codeql-analysis-call / Analyze (c-cpp)
  • GitHub Check: codeql-analysis-call / Analyze (python)
  • GitHub Check: Mergify Merge Protections
  • GitHub Check: Summary
⚠️ CI failures not shown inline (2)

GitHub Actions: Python Lint (Darker + Ruff) / darker-ruff-lint _ darker-ruff-lint: container: T7736: give container veths a deterministic host_interface_name

Conclusion: failure

View job details

##[group]Run echo "### 🧪 Lint Results"
 �[36;1mecho "### 🧪 Lint Results"�[0m
 �[36;1mdarker_failed="1"�[0m
 �[36;1mgraylint_failed=""�[0m
 �[36;1m�[0m
 �[36;1mif [[ "$darker_failed" == "1" ]]; then�[0m
 �[36;1m  echo "- ❌ **Darker** check failed"�[0m
 �[36;1melse�[0m
 �[36;1m  echo "- ✅ **Darker** check passed"�[0m
 �[36;1mfi�[0m
 �[36;1m�[0m
 �[36;1mif [[ "$graylint_failed" == "1" ]]; then�[0m
 �[36;1m  echo "- ❌ **Graylint (ruff check)** failed"�[0m
 �[36;1melse�[0m
 �[36;1m  echo "- ✅ **Graylint (ruff check)** passed"�[0m
 �[36;1mfi�[0m
 �[36;1m�[0m
 �[36;1mif [[ "$darker_failed" == "1" || "$graylint_failed" == "1" ]]; then�[0m
 �[36;1m  echo "::error::One or more linters failed. See above for details."�[0m

GitHub Actions: Python Lint (Darker + Ruff) / 0_darker-ruff-lint _ darker-ruff-lint.txt: container: T7736: give container veths a deterministic host_interface_name

Conclusion: failure

View job details

##[group]Run echo "### 🧪 Lint Results"
 �[36;1mecho "### 🧪 Lint Results"�[0m
 �[36;1mdarker_failed="1"�[0m
 �[36;1mgraylint_failed=""�[0m
 �[36;1m�[0m
 �[36;1mif [[ "$darker_failed" == "1" ]]; then�[0m
 �[36;1m  echo "- ❌ **Darker** check failed"�[0m
 �[36;1melse�[0m
 �[36;1m  echo "- ✅ **Darker** check passed"�[0m
 �[36;1mfi�[0m
 �[36;1m�[0m
 �[36;1mif [[ "$graylint_failed" == "1" ]]; then�[0m
 �[36;1m  echo "- ❌ **Graylint (ruff check)** failed"�[0m
 �[36;1melse�[0m
 �[36;1m  echo "- ✅ **Graylint (ruff check)** passed"�[0m
 �[36;1mfi�[0m
 �[36;1m�[0m
 �[36;1mif [[ "$darker_failed" == "1" || "$graylint_failed" == "1" ]]; then�[0m
 �[36;1m  echo "::error::One or more linters failed. See above for details."�[0m
🧰 Additional context used
📓 Path-based instructions (3)
**/*.py

📄 CodeRabbit inference engine (AGENTS.md)

**/*.py: Use ruff 0.6.4 for Python linting with configuration in ruff.toml at repository root
Use pylint to check for W0611 (unused imports) violations in Python code
Use darker for code formatting in Python files
Use nose2 for Python testing with configuration in nose2.cfg at repository root

Files:

  • smoketest/scripts/cli/test_container.py
  • src/conf_mode/container.py
smoketest/**/*.py

📄 CodeRabbit inference engine (AGENTS.md)

Runtime smoketests must be located under smoketest/ and use nose2 framework

Files:

  • smoketest/scripts/cli/test_container.py
src/conf_mode/*.py

📄 CodeRabbit inference engine (AGENTS.md)

Conf-mode entry-point scripts must be named after CLI components and located in src/conf_mode/

Files:

  • src/conf_mode/container.py
🧠 Learnings (3)
📚 Learning: 2026-05-26T06:03:59.703Z
Learnt from: c-po
Repo: vyos/vyos-1x PR: 5109
File: smoketest/scripts/cli/test_service_https.py:206-207
Timestamp: 2026-05-26T06:03:59.703Z
Learning: In VyOS smoketests that verify processes running inside a VRF using iproute2, remember that `ip vrf pids <vrf>` outputs one entry per line as `<pid> <process_name>` (e.g., `300431 nginx`), not PIDs alone. Therefore, assertions should check for the presence of the expected process name in the command output (e.g., `assertIn(PROCESS_NAME, cmd(f'ip vrf pids {vrf}'))`) rather than trying to match PID-only output.

Applied to files:

  • smoketest/scripts/cli/test_container.py
📚 Learning: 2026-05-26T06:04:29.163Z
Learnt from: c-po
Repo: vyos/vyos-1x PR: 5109
File: smoketest/scripts/cli/test_service_https.py:118-120
Timestamp: 2026-05-26T06:04:29.163Z
Learning: In VyOS smoketest scripts under `smoketest/scripts/cli/`, it is intentional to call `self.cli_delete(['vrf'])` in both `setUpClass` and `tearDown` to wipe the entire VRF subtree and ensure a clean slate. During code review, do not recommend narrowing the delete to specific VRF identifiers or name subsets (e.g., `['vrf', 'name', 'mgmt']`)—the broad teardown behavior is the established project-wide pattern for these tests.

Applied to files:

  • smoketest/scripts/cli/test_container.py
📚 Learning: 2026-06-29T12:13:51.293Z
Learnt from: andamasov
Repo: vyos/vyos-1x PR: 5298
File: smoketest/scripts/cli/test_vpp.py:0-0
Timestamp: 2026-06-29T12:13:51.293Z
Learning: When reviewing vyos-1x code that parses or asserts VPP CLI output (e.g., smoketest CLI tests and VPP op-mode code), do not flag the token spelling "Forwrd" / "U-Forwrd" as a typo. It is intentionally preserved verbatim from the upstream VPP CLI text shown by commands like `vppctl show bridge-domain ... detail`. This misspelling is centrally allowlisted (vyos/.github#153) for that specific VPP-CLI context, so typo-review comments should exclude "Forwrd" when it originates from that VPP output.

Applied to files:

  • smoketest/scripts/cli/test_container.py
🪛 ast-grep (0.44.1)
smoketest/scripts/cli/test_container.py

[warning] 289-289: File path is request-/variable-derived; validate and normalize to prevent path traversal.
Context: open(PROCESS_PIDFILE.format(name))
Note: [CWE-22] Improper Limitation of a Pathname to a Restricted Directory ('Path Traversal').

(open-filename-from-request)

🔍 Remote MCP Context7

Additional review context

  • Podman documents host_interface_name specifically for bridge networking as the interface name outside the container; interface_name is the container-side name.
  • Podman supports multiple networks by repeating --network; each network may include its own options.
  • For macvlan/ipvlan networks, the host-related interface setting identifies the parent device rather than a host-side veth pair.
  • The retrieved documentation does not establish when host_interface_name was introduced or confirm its availability specifically starting with Podman 5.8.
🔇 Additional comments (4)
src/conf_mode/container.py (4)

162-174: 🎯 Functional Correctness

Do not equate “non-macvlan” with bridge without enforcing that contract.

These changes treat every non-macvlan network as a paired-veth network. Podman’s host_interface_name is bridge-specific; for ipvlan or similar parent-based networks, the host-related setting refers to the parent device. Confirm the schema permits only bridge/macvlan here, or reject/classify other types before reserving and emitting this option.

Also applies to: 549-563

Source: MCP tools


378-396: 🎯 Functional Correctness

Make the interface-length check byte-aware.

len(prefix) counts Unicode code points, while Linux interface limits are byte-based. If container names are not explicitly ASCII-only, a non-ASCII name can pass the 15-character check and still produce an invalid Podman interface name. Enforce ASCII names or truncate/check encoded bytes.


549-563: 🩺 Stability & Availability

Verify Podman 5.8 supports host_interface_name.

The package requires Podman >=5.8, but the supplied documentation does not establish when this option was introduced. Confirm the minimum supported version accepts repeated --network name:host_interface_name=...; otherwise generated units may fail at startup on supported systems.

Source: MCP tools


125-125: LGTM!

Also applies to: 574-575, 598-598, 678-684

Comment on lines +267 to +293
def test_long_name_host_interface_uniqueness(self):
# T7736: the deterministic host-side veth interface name derived
# from a container name is truncated to fit IFNAMSIZ. Two distinct
# but similarly-prefixed long names must not truncate to the same
# interface name - Podman would then refuse to attach the second
# container's network, and its systemd unit would fail to start.
net_name = 'longiftest'
prefix = '192.0.2.0/24'
name_1 = 'abcdefghij-1'
name_2 = 'abcdefghij-2'

self.cli_set(base_path + ['network', net_name, 'prefix', prefix])
self.cli_set(base_path + ['name', name_1, 'image', busybox_image])
self.cli_set(base_path + ['name', name_1, 'network', net_name, 'address', str(ip_interface(prefix).ip + 2)])
self.cli_set(base_path + ['name', name_2, 'image', busybox_image])
self.cli_set(base_path + ['name', name_2, 'network', net_name, 'address', str(ip_interface(prefix).ip + 3)])
self.cli_commit()

# Both containers run a "conmon" process at once, so checking by
# process name alone can't distinguish which container it belongs
# to - verify each container's own recorded PID is still alive
for name in (name_1, name_2):
pid = 0
with open(PROCESS_PIDFILE.format(name)) as f:
pid = int(f.read())
self.assertTrue(os.path.exists(f'/proc/{pid}'))

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Assert the host-interface contract, not only PID existence.

This test can pass even if host_interface_name is removed: Podman may allocate two distinct vethN interfaces automatically. /proc/<pid> existence also does not prove that the expected container is running. Assert each container’s actual host-side interface is present and distinct, and validate container state through Podman or systemd.

🧰 Tools
🪛 ast-grep (0.44.1)

[warning] 289-289: File path is request-/variable-derived; validate and normalize to prevent path traversal.
Context: open(PROCESS_PIDFILE.format(name))
Note: [CWE-22] Improper Limitation of a Pathname to a Restricted Directory ('Path Traversal').

(open-filename-from-request)

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@smoketest/scripts/cli/test_container.py` around lines 267 - 293, The
test_long_name_host_interface_uniqueness test currently verifies only PID
existence, so it must also validate the host-interface contract and container
state. After cli_commit, obtain each container’s actual host-side interface
through the existing Podman or systemd inspection mechanism, assert both
interfaces are present and distinct, and verify each expected container is
running via Podman or systemd rather than relying on /proc/<pid> alone.

@l0crian1

l0crian1 commented Jul 25, 2026

Copy link
Copy Markdown
Contributor

@c-po

What do you think about rather than using a unique hash per name > 15 characters, you truncate in the middle using something like this?:

def truncate_middle(value, max_length=10):
    if len(value) <= max_length:
        return value

    remaining = max_length - 2  # Account for ".."
    left = (remaining + 1) // 2
    right = remaining // 2

    return value[:left] + ".." + value[-right:]

Then it'll keep the uniqueness of the name, while still being obvious to the user which interface is owned by which container.

print(truncate_middle("homeassistant1"))
print(truncate_middle("homeassistant2"))

home..ant1
home..ant2

Without that, the user would have to dig to find which interface belongs to homeassistant1, so it'd be annoying to try to use in something like firewall rules.

@c-po
c-po force-pushed the container-veth branch from c21e337 to 5a50249 Compare July 27, 2026 18:36
jestabro
jestabro previously approved these changes Jul 29, 2026

@jestabro jestabro left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A reasonable solution to naming collision. I would suggest considering @l0crian1 suggestion of preserving suffixes; coderabbit comment can be ignored, since it regards smoketest.

@c-po

c-po commented Aug 2, 2026

Copy link
Copy Markdown
Member Author

@c-po

What do you think about rather than using a unique hash per name > 15 characters, you truncate in the middle using something like this?:
...
Without that, the user would have to dig to find which interface belongs to homeassistant1, so it'd be annoying to try to use in something like firewall rules.

This is a compelling idea as my containers ar also numbered in an identical pattern like radius-1 or tacacs-1.

@c-po
c-po marked this pull request as draft August 3, 2026 14:54
@c-po

c-po commented Aug 3, 2026

Copy link
Copy Markdown
Member Author

@l0crian1 I see one disadvantage of this approach as some container instances could result in the same interface name. My idea is to combine both approaches. use veth-NAME-hash to make the interface name constant and hard to hit with a second name and also add a new op-mode command show container interfaces to get a list of container to interface mapping.

Moved this to DRAFT to implement this change.

@c-po
c-po marked this pull request as ready for review August 4, 2026 18:28
@c-po

c-po commented Aug 4, 2026

Copy link
Copy Markdown
Member Author

Updated PR which also adds a dependency on aardvark-dns (>=1.14.0)

@c-po
c-po requested a review from jestabro August 4, 2026 18:28
c-po added 2 commits August 4, 2026 20:36
…_name

Podman's default "vethN" auto-naming for a container's host-side veth can
collide with VyOS's own "virtual-ethernet vethN" interfaces.

Bump the minimum Podman dependency to 5.8 (which supports "host_interface_name"
network connect option) and use it to name every non-macvlan container network
attachment "veth-<container name>" instead, eliminating the collision by
construction. Container names too long to fit are shortened to a recognizable
prefix plus a short hash of the full name; verify() rejects the rare case
where two containers still generate the same interface name.

Add "show container interface" to display the resulting name-to-container
mapping.
…avark

aardvark-dns ships in lockstep with netavark (both 1.14.0 on the box this
was verified against), so pin it to the same >=1.14.0 minimum required for
Podman's "host_interface_name" network option to actually take effect.
@l0crian1

l0crian1 commented Aug 4, 2026

Copy link
Copy Markdown
Contributor

@c-po

I think using ConfigQuery for the show container interfaces could be slow on instances with large configs (at least based on my experience with other commands that need to do it).

What do you think of just redoing some of the work that is done in the conf_mode script, like checking length and creating hash if the length exceeds character limits? Maybe use something like these to return necessary lists (of course you can also do the JSON parsing in python if desired):

Get list of networks - This avoids host networking and macvlan networks

sudo podman network ls --format json | jq '[.[] | select(.driver == "bridge") | .name]'
Example:
sudo podman network ls --format json | jq '[.[] | select(.driver == "bridge") | .name]'
[
  "podman",
  "test"
]

Get a list of containers with network attachment, and return dict with container name:

sudo podman ps --format json | jq '[.[] | select(.Networks | length > 0) | .Names[0]]'
Example:
[
  {
    "name": "nb1",
    "networks": [
      "test"
    ]
  }
]

Then it's as simple as:

  • Check if the contents of "networks:" is in the list returned from the podman network ls command. If it is, an interface should be determined.
  • If the interface doesn't exceed character limits, then return the container name. If it does, return the same modified name with the digest.

As an alternative, you could probably iterate over the containers, and do podman inspect on them, looking for the net value in "CreateCommand":

@jestabro jestabro left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Re-approving, following changes since initial approval: (1) minor refactoring of conf-mode (2) addition of op-mode command; tested on built image.

PR includes comprehensive smoketests.

I'll leave it to @c-po to consider the most recent suggestion of @l0crian1 regarding the known issue of overhead of ConfigQuery.config_dict on large configs (since it requires parsing/instantiation of the full Config for what should be a simple op-mode command). Recall also the hacky workaround of using op_mode_config_dict from configquery.py which limits the parsing to the path in question, and consequently scales better; it may be used here since default values are not needed. I'm not advocating it over a simpler solution, simply a reminder of alternatives.

@mergify

mergify Bot commented Aug 5, 2026

Copy link
Copy Markdown
Contributor

Tick the box to add this pull request to the merge queue (same as @mergifyio queue).

  • Queue this pull request

@sever-sever sever-sever left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Deterministic veth naming looks correct, tests pass, no blockers to merge.

@c-po
c-po merged commit 636f74e into vyos:rolling Aug 5, 2026
28 of 32 checks passed
@c-po
c-po deleted the container-veth branch August 5, 2026 12:10
@vyos-bot vyos-bot Bot added mirror-initiated This PR initiated for mirror sync workflow mirror-completed and removed mirror-initiated This PR initiated for mirror sync workflow labels Aug 5, 2026
@github-actions

github-actions Bot commented Aug 6, 2026

Copy link
Copy Markdown

CI integration ❌ failed!

Details

CI logs

  • CLI Smoketests ❌ failed
  • CLI Smoketests (interfaces only) 👍 passed
  • Config tests 👍 passed
  • RAID1 tests 👍 passed
  • CLI Smoketests VPP 👍 passed
  • Config tests VPP 👍 passed
  • TPM tests 👍 passed

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Development

Successfully merging this pull request may close these issues.

4 participants